
#include "_gfx.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>

#include "_editbox.h"
#include "_font.h"
#include "editbox.h"
#include "_window.h"

#ifdef __DEBUG__
#define __E(fmt, args...) fprintf(stderr, "EDITBOX ERROR [%d]: " fmt, __LINE__, ## args)
#define __W(fmt, args...) fprintf(stderr, "EDITBOX WARNING [%d]: " fmt, __LINE__, ## args)
#else
#define __E(fmt, args...)
#define __W(fmt, args...)
#endif

#define false       0
#define true        1

static editbox edits[EDITBOX_MAX_NUMBER];
static int ebId = 0;

int editbox_create(unsigned int x, unsigned int y, unsigned int width,
        unsigned int r, unsigned int g, unsigned int b, unsigned char *text,
        unsigned int font_height, unsigned int font_r, unsigned int font_g,
        unsigned int font_b, unsigned int border) {

    if (ebId > EDITBOX_MAX_NUMBER) {
        __E("Nao e possivel criar mais editboxes. Numero maximo.\n");
        return EDITBOX_FAILURE;
    } else {
        edits[ebId].x = x;
        edits[ebId].y = y;
        edits[ebId].h = font_height + (unsigned int) font_height * 0.7;
        edits[ebId].w = width;
        edits[ebId].bg_r = r;
        edits[ebId].bg_g = g;
        edits[ebId].bg_b = b;
        edits[ebId].font_r = font_r;
        edits[ebId].font_g = font_g;
        edits[ebId].font_b = font_b;
        edits[ebId].border = border;
        edits[ebId].selected = false;
        edits[ebId].visible = true;
        edits[ebId].initialized = false;
        edits[ebId].editable = true;
        edits[ebId].mode = EDITBOX_MODE_TEXT;
        edits[ebId].id = ebId;
        edits[ebId].font_height = font_height;
        strcpy(edits[ebId].text, text);
        edits[ebId].winp = NULL;
        edits[ebId].initialized = true;
        return ebId++;
    }
}

signed int editbox_destroy(signed int editboxId) {
    if (!edits[editboxId].initialized) {
        __E("Editbox nao pode ser destruido: editbox nao foi criado.");
        return EDITBOX_FAILURE;
    }

    edits[editboxId].x = 0;
    edits[editboxId].y = 0;
    edits[editboxId].h = 0;
    edits[editboxId].w = 0;
    edits[editboxId].bg_r = 0;
    edits[editboxId].bg_g = 0;
    edits[editboxId].bg_b = 0;
    edits[editboxId].font_r = 0;
    edits[editboxId].font_g = 0;
    edits[editboxId].font_b = 0;
    edits[editboxId].border = 0;
    edits[editboxId].font_height = 0;
    edits[editboxId].redraw = 0;
    edits[editboxId].selected = 0;
    edits[editboxId].visible = 0;
    strncpy(edits[editboxId].text, "", EDITBOX_MAX_TEXT);
    edits[editboxId].text[EDITBOX_MAX_TEXT - 1] = '\0';

    editbox_delFromWindow(editboxId);
    edits[editboxId].initialized = 0;
    return EDITBOX_SUCCESS;
}

/* editbox_draw desenha um editbox no buffer da janela a que ele esta associado */
int editbox_draw(int editboxId) {
	char text[EDITBOX_MAX_TEXT];

    if (edits[editboxId].initialized) {
        if (edits[editboxId].winp) {
            if (edits[editboxId].visible) {
                unsigned char selected_color_offset = edits[editboxId].selected ? EDITBOX_SELECTED_OFFSET : 0;
                gfx_render_box(edits[editboxId].w, edits[editboxId].h,
                        edits[editboxId].x, edits[editboxId].y,
                        edits[editboxId].border, edits[editboxId].winp,
                        edits[editboxId].bg_r + selected_color_offset,
                        edits[editboxId].bg_g + selected_color_offset,
                        edits[editboxId].bg_b + selected_color_offset);

                unsigned int string_width, string_height, font_x, font_y;
                string_width = font_getStringWidth(edits[editboxId].text, edits[editboxId].font_height);
                if (string_width > (edits[editboxId].w - 2 * edits[editboxId].border)) {
                    __E("Texto mais largo que o editbox!\n");
                    return EDITBOX_FAILURE;
                }
                string_height = font_getStringHeight(edits[editboxId].text, edits[editboxId].font_height);

                /* Este calculo centralizaria a fonte no editbox */
                /*font_x = edits[editboxId].x + edits[editboxId].w / 2 - string_width / 2;*/

                font_x = edits[editboxId].x + 2 * edits[editboxId].border;
                font_y = edits[editboxId].y + edits[editboxId].h / 2 + string_height / 2;

				memset(text, 0, EDITBOX_MAX_TEXT);
				if (edits[editboxId].mode == EDITBOX_MODE_PASSWORD) {
					memset(text, '*', strlen(edits[editboxId].text));
				} else {
					strcpy(text, edits[editboxId].text);
				}

                if (font_render(text, font_x, font_y, 0xff, edits[editboxId].font_height,
                        edits[editboxId].winp, edits[editboxId].font_r, edits[editboxId].font_g,
                        edits[editboxId].font_b) < 0) {
                    __E("Falha na renderizacao da fonte do editbox!\n");
                    return EDITBOX_FAILURE;
                } else return EDITBOX_SUCCESS;
            } else return EDITBOX_SUCCESS;
        } else {
            __E("Nenhuma tela associada ao editbox.\n");
            return EDITBOX_FAILURE;
        }
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

/* editbox_unselect nao atualiza mais o buffer da tela */
int editbox_unselect(int editboxId) {
    if (edits[editboxId].initialized) {
        if (edits[editboxId].winp) {
            edits[editboxId].selected = false;
            window_set_actualEditbox_selected(edits[editboxId].windowId, -1);
            return EDITBOX_SUCCESS;
        } else {
            __E("Nenhuma tela asociada ao editbox.\n");
            return EDITBOX_FAILURE;
        }
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

/* editbox_select nao atualiza mais o buffer da tela */
int editbox_select(int editboxId) {
    if (edits[editboxId].initialized) {
        if (edits[editboxId].winp) {
            if (edits[editboxId].visible) {
                int aux = window_inform_actualEditbox(edits[editboxId].windowId);
                if (aux < 0) return EDITBOX_FAILURE;
                do {
                    if (edits[aux].selected == true)
                        editbox_unselect(aux);
                    aux = editbox_next(aux);
                } while (aux != window_inform_actualEditbox(edits[editboxId].windowId));
                edits[editboxId].selected = true;
                window_set_actualEditbox_selected(edits[editboxId].windowId, editboxId);
                return EDITBOX_SUCCESS;
            } else return EDITBOX_SUCCESS;
        } else {
            __E("Nenhuma tela associada ao editbox.\n");
            return EDITBOX_FAILURE;
        }
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

int editbox_read(int editboxId) {
    if (edits[editboxId].initialized) {
        return edits[editboxId].selected;
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

int editbox_setBorder(int editboxId, unsigned int border) {
    if (edits[editboxId].initialized) {
        if (border >= 0) {
            edits[editboxId].border = border;
            return EDITBOX_SUCCESS;
        }
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

int editbox_setVisible(int editboxId, unsigned char visible) {
    if (edits[editboxId].initialized) {
        if (edits[editboxId].winp) {
            if (visible == true || visible == false) {
                edits[editboxId].visible = visible;
            } else {
                __E("Opcao invalida.\n");
                return EDITBOX_FAILURE;
            }
        } else {
            __E("Nenhuma tela associada ao editbox.\n");
            return EDITBOX_FAILURE;
        }
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

int editbox_setText(int editboxId, unsigned char *text) {
    if (edits[editboxId].initialized) {
        if (edits[editboxId].editable) {
            char *temp;
            if (strlen(text) >= 0) {
                if (font_getStringWidth(text, edits[editboxId].font_height) > (edits[editboxId].w - 2 * edits[editboxId].border)) {
                    __E("Frase maior que o campo.\n");
                    return EDITBOX_TEXT_EXCEED;
                }
            }
            switch (edits[editboxId].mode) {
                case EDITBOX_MODE_TEXT:
                    strcpy(edits[editboxId].text, text);
                    edits[editboxId].text[strlen(text)] = '\0';
                    break;
                case EDITBOX_MODE_NUMERIC:
                    temp = text;
                    while (*text) {
                        if ((*text < 0x30 || *text > 0x39) && *text != ',' && *text != '.') {
                            __E("Entrada de dados invalida (modo numerico).\n");
                            return EDITBOX_FAILURE;
                        }
                        text++;
                    }
                    text = temp;
                    strcpy(edits[editboxId].text, text);
                    edits[editboxId].text[strlen(text)] = '\0';
                    break;
                case EDITBOX_MODE_PASSWORD:
                    temp = text;
                    while (*text) {
                        if (*text < 0x30 || *text > 0x39) {
                            __E("Entrada de dados invalida (modo password).\n");
                            return EDITBOX_FAILURE;
                        }
                        text++;
                    }
                    text = temp;
                    strcpy(edits[editboxId].text, text);
                    edits[editboxId].text[strlen(text)] = '\0';
                    break;
                default:
                    return EDITBOX_FAILURE;
                    break;
            }
            return EDITBOX_SUCCESS;
        } else {
            __E("Editbox nao pode ser editado (editable == false).\n");
            return EDITBOX_FAILURE;
        }
    } else {
        __E("editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

int editbox_setEditable(int editboxId, unsigned char editable) {
    if (edits[editboxId].initialized) {
        if (editable == true || editable == false) {
            edits[editboxId].editable = editable;
            return EDITBOX_SUCCESS;
        } else {
            __E("Opcao invailda.\n");
            return EDITBOX_FAILURE;
        }
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

int editbox_setMode_numeric(int editboxId) {
    if (edits[editboxId].initialized) {
        edits[editboxId].mode = MODE_NUMERIC;
        return EDITBOX_SUCCESS;
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

int editbox_setMode_password(int editboxId) {
    if (edits[editboxId].initialized) {
        edits[editboxId].mode = MODE_PASSWORD;
        return EDITBOX_SUCCESS;
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

int editbox_setMode_text(int editboxId) {
    if (edits[editboxId].initialized) {
        edits[editboxId].mode = MODE_TEXT;
        return EDITBOX_SUCCESS;
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

unsigned char editbox_getMode(int editboxId) {
    if (edits[editboxId].initialized) return edits[editboxId].mode;
    else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

// Retorna sucesso e preenche txt com o texto do editbox, ou retorna falha e zera txt

int editbox_getText(int editboxId, unsigned char *txt) {
    if (edits[editboxId].initialized) {
        strcpy(txt, edits[editboxId].text);
        return EDITBOX_SUCCESS;
    } else {
        __E("Editbox nao foi criado.\n");
        txt[0] = '\0';
        return EDITBOX_FAILURE;
    }
}

int editbox_addToWindow(int editboxId, int editboxId_next, int windowId, window *winp) {
    if (edits[editboxId_next].winp == NULL) {
        edits[editboxId_next].next = edits[editboxId].next;
        edits[editboxId].next = &edits[editboxId_next];
        edits[editboxId_next].previous = &edits[editboxId];
        edits[editboxId_next].next->previous = &edits[editboxId_next];
        edits[editboxId_next].winp = winp;
        edits[editboxId_next].windowId = windowId;
        return EDITBOX_SUCCESS;
    } else {
        __E("Ja existe uma tela associada ao editbox.\n");
        return EDITBOX_FAILURE;
    }
}

signed int editbox_delFromWindow(signed int editboxId) {
    if (!edits[editboxId].initialized) {
        __E("Editbox nao criado.\n");
        return EDITBOX_FAILURE;
    }

    if (edits[editboxId].winp == NULL) {
        return EDITBOX_SUCCESS;
    }

    if (edits[editboxId].next == &edits[editboxId]) {
        edits[editboxId].winp->firstEditbox = -1;
        edits[editboxId].winp->actualEditbox = -1;
    } else {
        if (edits[editboxId].winp->actualEditbox == editboxId) {
            edits[editboxId].winp->actualEditbox = edits[edits[editboxId].winp->actualEditbox].previous->id;
        } else if (edits[editboxId].winp->firstEditbox == editboxId) {
            edits[editboxId].winp->firstEditbox = edits[edits[editboxId].winp->firstEditbox].next->id;
        }

        edits[editboxId].previous->next = edits[editboxId].next;
        edits[editboxId].next->previous = edits[editboxId].previous;
    }

    if (edits[editboxId].winp->actualEditbox_selected == edits[editboxId].id) {
        edits[editboxId].winp->actualEditbox_selected = -1;
    }

    edits[editboxId].previous = &edits[editboxId];
    edits[editboxId].next = &edits[editboxId];
    edits[editboxId].winp = NULL;
    edits[editboxId].windowId = -1;
    window_setComponentsRedraw(edits[editboxId].windowId);

    return EDITBOX_SUCCESS;
}

int editbox_previous(int editboxId) {
    if (edits[editboxId].initialized) return edits[editboxId].previous->id;
    else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

int editbox_next(int editboxId) {
    if (edits[editboxId].initialized) return edits[editboxId].next->id;
    else {
        __E("editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

void editbox_setFontColor(int editboxId, unsigned int font_red, unsigned int font_green, unsigned int font_blue) {
    if (edits[editboxId].initialized) {
        edits[editboxId].font_r = font_red;
        edits[editboxId].font_g = font_green;
        edits[editboxId].font_b = font_blue;
    } else return;
}

signed int editbox_getX(signed int editboxId) {
    if (edits[editboxId].initialized) {
        return edits[editboxId].x;
    } else {
        __E("Editbox não foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

signed int editbox_getY(signed int editboxId) {
    if (edits[editboxId].initialized) {
        return edits[editboxId].y;
    } else {
        __E("Editbox não foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

unsigned int editbox_getWidth(int editboxId) {
    if (edits[editboxId].initialized) {
        return edits[editboxId].w;
    } else {
        __E("Editbox não foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

signed int editbox_getHeight(signed int editboxId) {
    if (edits[editboxId].initialized) {
        return edits[editboxId].h;
    } else {
        __E("Editbox não foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

unsigned int editbox_getBorder(int editboxId) {
    if (edits[editboxId].initialized) {
        return edits[editboxId].border;
    } else {
        __E("Editbox não foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

void editbox_setRedraw(signed int editboxId) {
    edits[editboxId].redraw = 1;
}

signed int editbox_setColor(signed int editboxId, unsigned int r, unsigned int g, unsigned int b) {
    if (edits[editboxId].initialized) {
        edits[editboxId].bg_r = r;
        edits[editboxId].bg_g = g;
        edits[editboxId].bg_b = b;
        edits[editboxId].redraw = 1;
        return EDITBOX_SUCCESS;
    } else {
        __E("Editbox nao foi criado.\n");

        return EDITBOX_FAILURE;
    }
}

signed int editbox_getFontHeight(signed int editboxId) {
    if (edits[editboxId].initialized) {
        return edits[editboxId].font_height;
    } else {
        __E("Editbox não foi criado.\n");
        return EDITBOX_FAILURE;
    }
}

signed int editbox_isSelected(signed int editboxId) {
    if (edits[editboxId].initialized) {
        return edits[editboxId].selected;
    } else {
        __E("Editbox nao foi criado.\n");
        return EDITBOX_FAILURE;
    }
}
